package com.modern.devtools.service;

import com.modern.devtools.interfaces.MonitorStrategy;
import com.modern.devtools.model.Server;
import com.modern.devtools.model.ServiceStatus;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Monitor Service - manages service monitoring tasks
 * Handles periodic checking of service status across servers
 */
@Service
public class MonitorService {
    
    private final ObservableList<ServiceStatus> serviceStatuses = FXCollections.observableArrayList();
    private final Map<String, ServiceStatus> statusMap = new ConcurrentHashMap<>();
    private final Map<String, Boolean> monitoringTasks = new ConcurrentHashMap<>();
    
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
    
    @Autowired
    private ServerService serverService;
    
    /**
     * Adds a service to be monitored
     * @param serviceId The ID of the service
     * @param serviceName The name of the service
     * @param serverId The ID of the server where the service runs
     * @param strategy The monitoring strategy to use
     * @param params Additional parameters for the monitoring strategy
     */
    public void addServiceToMonitor(String serviceId, String serviceName, String serverId, MonitorStrategy strategy, Object... params) {
        ServiceStatus status = new ServiceStatus(serviceId, serviceName, serverId);
        statusMap.put(serviceId, status);
        serviceStatuses.add(status);
        
        // Start monitoring if auto-start is enabled
        if (monitoringTasks.getOrDefault(serviceId, false)) {
            startMonitoring(serviceId, strategy, 30, params); // Default to 30-second intervals
        }
    }
    
    /**
     * Starts monitoring a service with the specified strategy
     * @param serviceId The ID of the service to monitor
     * @param strategy The monitoring strategy to use
     * @param intervalSeconds The interval between checks in seconds
     * @param params Additional parameters for the monitoring strategy
     */
    public void startMonitoring(String serviceId, MonitorStrategy strategy, int intervalSeconds, Object... params) {
        if (monitoringTasks.put(serviceId, true)) {
            // Already monitoring, stop first
            stopMonitoring(serviceId);
        }
        
        scheduler.scheduleAtFixedRate(() -> {
            try {
                ServiceStatus currentStatus = statusMap.get(serviceId);
                if (currentStatus != null) {
                    // Get the server for this service
                    var serverOpt = serverService.getServerById(currentStatus.getServerId());
                    if (serverOpt.isPresent()) {
                        // For now, just update the status using the strategy
                        // In a real implementation, we would pass server information to the strategy
                        ServiceStatus newStatus = strategy.checkStatus(
                            currentStatus.getServiceId(),
                            currentStatus.getServiceName(),
                            currentStatus.getServerId(),
                            params
                        );
                        
                        // Update the status in our map
                        statusMap.put(serviceId, newStatus);
                        
                        // Update the observable list
                        int index = serviceStatuses.indexOf(currentStatus);
                        if (index != -1) {
                            serviceStatuses.set(index, newStatus);
                        } else {
                            // If not found, add it
                            serviceStatuses.add(newStatus);
                        }
                    }
                }
            } catch (Exception e) {
                System.err.println("Error monitoring service " + serviceId + ": " + e.getMessage());
                e.printStackTrace();
            }
        }, 0, intervalSeconds, TimeUnit.SECONDS);
    }
    
    /**
     * Stops monitoring a service
     * @param serviceId The ID of the service to stop monitoring
     */
    public void stopMonitoring(String serviceId) {
        monitoringTasks.put(serviceId, false);
        // In a real implementation, we would cancel the scheduled task
        // For this implementation, we're using a simple boolean flag
    }
    
    /**
     * Gets all monitored service statuses
     * @return ObservableList of all service statuses
     */
    public ObservableList<ServiceStatus> getAllServiceStatuses() {
        return FXCollections.unmodifiableObservableList(serviceStatuses);
    }
    
    /**
     * Gets the status of a specific service
     * @param serviceId The ID of the service
     * @return The service status if found
     */
    public ServiceStatus getServiceStatus(String serviceId) {
        return statusMap.get(serviceId);
    }
    
    /**
     * Removes a service from monitoring
     * @param serviceId The ID of the service to remove
     */
    public void removeServiceFromMonitor(String serviceId) {
        stopMonitoring(serviceId);
        ServiceStatus status = statusMap.remove(serviceId);
        if (status != null) {
            serviceStatuses.remove(status);
        }
    }
    
    /**
     * Gets the number of services being monitored
     * @return Number of monitored services
     */
    public int getMonitoredServiceCount() {
        return serviceStatuses.size();
    }
    
    /**
     * Gets the number of services currently UP
     * @return Number of UP services
     */
    public int getUpServiceCount() {
        return (int) serviceStatuses.stream()
                .filter(status -> "UP".equals(status.getStatus()))
                .count();
    }
}